package linker

import (
	"risc-v/ld/pkg/utils"
)

// Symbol 和 ELFSymbol 所指定的数据块关联
// 可以通过 Symbol 获取 ELFSymbol
type Symbol struct {
	File            *ObjectFile   // 可能所属的 ObjectFile ，即 .o 文件,如果没有则为 nil
	InputSection    *InputSection // 可能所属的 Section 如果没有则为 nil
	Name            string
	Value           uint64 // 和 ELFSymbol.Val 对应
	SymbolIndex     int    // SymbolTable 中的索引,即 File.ElfSymbols 中的索引
	SectionFragment *SectionFragment

	GotTpIndex int32
	Flags      uint32
}

// NewSymbol  Symbol 的构造函数
func NewSymbol(name string) *Symbol {
	return &Symbol{
		Name:        name,
		SymbolIndex: -1, // 默认值 -1 表示非法
	}
}

// SetInputSection 根据提供的 InputSection 对 Symbol 进行对应的赋值操作
func (s *Symbol) SetInputSection(sec *InputSection) {
	s.InputSection = sec
	s.SectionFragment = nil
}

func (s *Symbol) SetSectionFragment(frag *SectionFragment) {
	s.InputSection = nil
	s.SectionFragment = frag
}

// ELFSymbol 根据 Symbol 自身的 SymbolIndex 从 ObjectFile 中获取 ELFSymbol 数据
// 该函数内部会对 Symbol.SymbolIndex 进行越界判断
// utils.Assert(s.SymbolIndex < len(s.File.ElfSymbols))
func (s *Symbol) ELFSymbol() *ELFSymbol {
	//对 SymbolIndex 进行断言 防止越界
	utils.Assert(s.SymbolIndex < len(s.File.ElfSymbols))
	return &s.File.ElfSymbols[s.SymbolIndex]
}

// Clear 清除 Symbol 中的 File、 InputSection、 SymbolIndex 数据
// File = nil 和 InputSection = nil  SymbolIndex = -1
func (s *Symbol) Clear() {
	s.File = nil
	s.InputSection = nil
	s.SymbolIndex = -1
}

// GetSymbolByName 根据 name 获取 存储在 Context.SymbolMap 中的值
// 如果存在,直接返回 否则使用 NewSymbol() 创建，并加入 Context.SymbolMap
func GetSymbolByName(ctx *Context, name string) *Symbol {
	if sym, ok := ctx.SymbolMap[name]; ok {
		return sym
	}
	ctx.SymbolMap[name] = NewSymbol(name)
	return ctx.SymbolMap[name]
}

func (s *Symbol) GetAddr() uint64 {
	if s.SectionFragment != nil {
		return s.SectionFragment.GetAddr() + s.Value
	}

	if s.InputSection != nil {
		return s.InputSection.GetAddr() + s.Value
	}

	return s.Value
}

func (s *Symbol) GetGotTpAddr(ctx *Context) uint64 {
	return ctx.Got.SectionHeader.Addr + uint64(s.GotTpIndex)*8
}
